package fhv.itb.ec.fruitshop;

import java.util.Collection;
import java.util.HashMap;
import java.util.Vector;
import java.util.List;
import java.util.Random;
import javax.annotation.PostConstruct;
import javax.ejb.EJB;
import javax.ejb.Stateful;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

@Stateful
public class FruitBasketBean implements FruitBasketRemote, FruitBasketLocal {

    @PersistenceContext(unitName = "Fruits-ejbPU")
    private EntityManager em;
    @EJB
    FruitFacadeLocal ff;
    
    /**
     * Fruits which are currently in the basket.
     */
    private HashMap<Long, Fruit> m_fruits;

    /**
     * Shop, where basket is right now.
     */
    private int m_shopId;
    private FruitShopEntity m_fruitShop;

    public FruitBasketBean() {
        m_shopId = 0;
    }

    @PostConstruct
    private void init() {
        m_fruits = new HashMap<Long, Fruit>();
    }


    // ----- Fruit Shop operations ----- //
    /**
     * Gets a list of all the shops which are available for shopping.
     *
     * @return a vector of shop ids.
     */
    public Vector<Long> getShopList() {
        List<FruitShopEntity> shops = findAll();
        Vector<Long> ids = new Vector<Long>();

        // make sure that the database is initialized
        if (shops == null || shops.isEmpty()) {
            initShops();
        }
        shops = findAll();
        for (FruitShopEntity fse : shops) {
            ids.add(fse.getId());
        }

        return ids;
    }

    public void create(FruitShopEntity fruitShopEntity) {
        em.persist(fruitShopEntity);
    }

    public void edit(FruitShopEntity fruitShopEntity) {
        em.merge(fruitShopEntity);
    }

    public void remove(FruitShopEntity fruitShopEntity) {
        em.remove(em.merge(fruitShopEntity));
    }

    public FruitShopEntity find(Object id) {
        return em.find(fhv.itb.ec.fruitshop.FruitShopEntity.class, id);
    }

    public List<FruitShopEntity> findAll() {
        return em.createQuery("select object(o) from FruitShopEntity as o").getResultList();
    }
    // ----- End Fruit Shop operations ----- //


    // ----- Basket operations ----- //
    /**
     * Select the shop where the client is currently shopping.
     * 
     * @param shopID
     */
    public void selectShop(int shopID) {
        Vector<Long> shops = getShopList();
        Long longShopId = new Long(shopID);

        if (shops.contains(longShopId)) {
            m_shopId = shopID;
            m_fruitShop = em.find(FruitShopEntity.class, new Long(m_shopId));
        } else {
            throw new java.lang.UnsupportedOperationException("Shop with id: " + shopID + " is not supported");
        }

    }

    public int getShopID() {
        return m_shopId;
    }

    /**
     * Lists all the fruits available in the current shop.
     * 
     * @return a Vector containing the information.
     */
    public Vector<String> listAvailableFruits() {
        Vector<String> result = new Vector<String>();

        if (m_fruitShop != null) {
            List<Fruit> fruits = m_fruitShop.getFruits();

            if (fruits != null) {

                for (Fruit f : fruits) {
                    result.add(f.getId() + ": " + f.getName() + " " +
                            f.getClass().getSimpleName() + ", EUR " + ((double) f.getPrice()) / 100);
                }

            }

        }

        return result;
    }

    /**
     * Gets the total number of fruits in the basket.
     * 
     * @return
     * The total number of fruits in the basket.
     */
    public int getNumOfFruits() {
        return m_fruits.size();
    }

    /**
     * Gets the total cost of all the fruits in the basket.
     * 
     * @return
     * The total cost of all the fruits in the basket.
     */
    public double getCostOfFruits() {

        Collection c = m_fruits.values();
        Object[] a = c.toArray();
        double costofFruits = 0;
        for (int i = 0; i < a.length; i++) {
            costofFruits = costofFruits + ((Fruit) a[i]).getPrice();
        }
        return costofFruits / 100;
    }

    /**
     * Gets a list of all the fruits in the basket.
     * 
     * @return
     * A list of all the fruits in the basket.
     */
    public String getContents() {

        String contents = "";

        Collection c = m_fruits.values();
        Object[] a = c.toArray();

        for (int i = 0; i < a.length; i++) {
            contents = contents + ((Fruit) a[i]).getNumber() + " pieces of " + ((Fruit) a[i]).getName() + " " + ((Fruit) a[i]).getClass().getSimpleName() + '\n';
        }

        return contents;
    }

    /**
     * Adds the specified fruit to the basket.
     * 
     * @param id The ID of the fruit to be added.
     * @param amount The amount of this fruit to be added.
     */
    public void addFruit(int fruitID) {

        if (m_fruitShop != null) {
            Fruit f = m_fruitShop.getFruit(new Long(fruitID));

            if (f != null && f.getNumber() > 0) {

                // check if this fruit is already in the basket
                if (m_fruits.containsKey(f.getId())) {
                    // increment the amount
                    m_fruits.get(f.getId()).setNumber(m_fruits.get(f.getId()).getNumber() + 1);
                } else {
                    // put a copy in basket
                    String type = f.getClass().getSimpleName();
                    Fruit f2 = null;

                    if (type.equals("Apple")) {
                        f2 = new Apple(f);
                    } else if (type.equals("Banana")) {
                        f2 = new Banana(f);
                    } else if (type.equals("Pear")) {
                        f2 = new Pear(f);
                    }

                    if (f2 != null) {
                        f2.setNumber(1);
                        m_fruits.put(f2.getId(), f2);
                    }

                }
                // update db
                f.setNumber(f.getNumber() - 1);
                ff.edit(f);
                //edit(m_fruitShop);
                // update (reload) the fruit shop
                m_fruitShop = em.find(FruitShopEntity.class, new Long(m_shopId));
            }

        }

    }

    /**
     * Removes the specified fruit from the basket.
     * This will only succeed, if the basket is currently in the shop,
     * where the fruit came from.
     * 
     * @param id The ID of the fruit to be removed.
     * @param amount The amount of this fruit to be removed.
     * @return true, if the frut was removed from basket and put back
     * into the shop.
     */
    public boolean removeFruit(int fruitID) {
        boolean result = false;

        if (m_fruits != null && m_fruits.containsKey(new Long(fruitID))) {
            Fruit f = m_fruits.get(new Long(fruitID));

            if (f != null) {
                int num = f.getNumber();
                // remove from basket
                if (num > 0) {
                    f.setNumber(num - 1);
                    // update in shop
                    Fruit f2 = m_fruitShop.getFruit(new Long(fruitID));

                    // put the fruit back
                    if (f2 != null) {
                        f2.setNumber(f2.getNumber() + 1);
                        ff.edit(f2);
                        result = true;
                    }

                } else {
                    m_fruits.remove(fruitID);
                }
            }


        } else {
            System.out.println("Fruit not found in basket");
        }
        // refresh the shop
        m_fruitShop = em.find(FruitShopEntity.class, new Long(m_shopId));

        return result;
    }

    /**
     * Purchase all the fruits in the basket.
     * 
     * @return
     * true, if the purchase was successful, false otherwise.
     */
    public boolean buyFruits() {
        
        if (!m_fruits.isEmpty()) {
            m_fruits.clear();
            return true;
        }

        return false;
    }

    // ----- End Basket operations ----- //
    /**
     * Initializes the database, if this is not already done.
     */
    private void initShops() {
        Random r = new Random();

        for (int i = 1; i < 4; i++) {
            FruitShopEntity shop = new FruitShopEntity();

            // put fruits in the shop
            for (int j = 0; j < 5; j++) {
                Apple a = new Apple();
                a.setName("Elstar");
                a.setNumber(r.nextInt(100));
                a.setPrice((int) ((i * r.nextInt(100)) % 350));
                ff.create(a);
                shop.putFruit(a);

                Apple a2 = new Apple();
                a2.setName("Granny Smith");
                a2.setNumber(r.nextInt(50));
                a2.setPrice((int) ((i * r.nextInt(100)) % 400));
                ff.create(a2);
                shop.putFruit(a2);

                Pear p = new Pear();
                p.setName("Williams");
                p.setNumber(r.nextInt(50));
                p.setPrice((int) ((i * r.nextInt(100)) % 500));
                ff.create(p);
                shop.putFruit(p);

                Pear p2 = new Pear();
                p2.setName("Subira");
                p2.setNumber(r.nextInt(50));
                p2.setPrice((int) ((i * r.nextInt(100)) % 440));
                ff.create(p2);
                shop.putFruit(p2);

                Banana b = new Banana();
                b.setName("Chiquita");
                b.setNumber(r.nextInt(70));
                b.setPrice((int) ((i * r.nextInt(100)) % 500));
                ff.create(b);
                shop.putFruit(b);

                Banana b2 = new Banana();
                b2.setName("Clever");
                b2.setNumber(r.nextInt(80));
                b2.setPrice((int) ((i * r.nextInt(100)) % 440));
                ff.create(b2);
                shop.putFruit(b2);
            }

            // persist the shop
            create(shop);
        }

    }
}
